<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // call , apply , bind 是js里面改变函数this的指向
        var name = 'codereasy'

        let obj = {
            name: '张三',
            say: function () {
                console.log(this.name);
            },
            say2: function (p1, p2) {
                console.log(p1 + ',' + p2 + ',' + this.name);
            },
            say3: () => {
                console.log(this.name); //codereasy, 箭头函数没有自己的this,只能找到外围离函数最近的this
            }
        }
        obj.say() // 张三
        setTimeout(obj.say, 10) // codereasy--因为没有指定上下文是谁

        setTimeout(() => {
            // apply,call 都是立即执行
            obj.say2.apply(this, ['hello', 'world']) // hello,world,张三
            obj.say2.call(this, 'hello', 'world') // hello,world,张三

            obj.say3.call(this)

            // bind 不会立即执行，返回的是一个新的函数，而且不需要将参数一次性传入
            // 方式1
            // let newFun = obj.say2.bind(obj,'hello','world') 
            // newFun() // hello,world,张三

            // 方式2
            let newFun = obj.say2.bind(this)
            newFun('hello', 'world')  // hello,world,张三
        }, 10)


        /***
         * 总结 相同点1 都是函数.call(),函数.apply(),函数.bind()
         *           2 都会改变this的指向
         * 
         * 区别 1: call和apply会立即执行, bind不会，他是返回一个函数，调用去执行
         *      2: call(this,parmas1,parmas2), apply(this,[parmas1,parmas2]) apply的参数是放在 数组里面的,call 不用
         *      3: bind是 永久绑定
         * ***/

        //  为啥要改变this的指向，目的有 1、实现方法复用， 2、处理动态参数
        //  apply应用场景
        const numbers = [23, 45, 12, 67, 89, 34]
        const max = Math.max.apply(null, numbers)
        const max2 = Math.max(numbers)
        console.log('max', max); // 89
        console.log('max2', max2); // NnN 因为number是个数组，但是 Math.max()不需要数组格式,要下面的格式
        console.log('max3', Math.max(23, 45, 12, 67, 89, 34)); // 89

        const arr1 = [1, 2];
        const arr2 = [3, 4];
        Array.prototype.push.apply(arr1, arr2); // [1, 2, 3, 4]
        // Array.prototype.concat.apply(arr1, arr2); // [1,2]
        console.log('arr1', arr1);

        // bind 的应用
        // 1 解决事件监听中的 this 问题
        class Button {
            constructor() {
                this.text = "Click me";
                this.element = document.createElement("button");
                this.element.innerHTML = '你打我呀'
                // 绑定 this 到当前实例
                this.element.addEventListener("click", this.onClick.bind(this));
            }

            onClick() {
                console.log(this.text); // 正确输出 "Click me"
            }
        }

        const btn = new Button();
        document.body.appendChild(btn.element);

        //2setTimeout 配合避免 this 丢失

        const counter = {
        count: 0,
        start: function() {
            setInterval(function() {
            // 未绑定 this 时，this 指向全局对象
            this.count++;
            console.log(this.count);
            }.bind(this), 1000); // 绑定 this 到 counter，没有bind(this)打印出来就是NaN
        }
        };
        // counter.start(); // 每秒输出递增的 count 值

    </script>
</body>

</html>